// IOBeepInspector.m
// A system beep inspector
// Copyright 1993 by NeXT, Incorporated.  All Rights Reserved.

// This is not a subclass of IODeviceInspector, since it does not need
// to access any of the standard controls.  Therefore, we must implement
// our own loading of the nib, and otherwise conform to the
// IOConfigurationInspector protocol.

#import "IOBeepInspector.h"

#import <driverkit/IODeviceMaster.h>
#import <appkit/Control.h>
#import <stdlib.h>

#define DRIVER_NAME "Beep"
#define PITCH_KEY "Frequency"
#define DURATION_KEY "Duration"
#define STYLE_KEY "Style"

@implementation IOBeepInspector

// We're a subclass of Object, so this is the only init necessary
- init
{
    char  buff[MAXPATHLEN];
    IOReturn ret;
    IOString kind;
    
    if (![[NXBundle bundleForClass: [self class]] getPath: buff
	    forResource:"Inspector" ofType:"nib"])
    {
	NXLogError("Couldn't load inspector nib");
	[self free];
	return nil;
    }
    if (![NXApp loadNibFile:buff owner:self withNames:NO]) {
	[self free];
	return nil;
    }
    // Note the dichotomy here...
    stylePopup = [styleButton target];
    [stylePopup setAction:@selector(styleDidChange:)];
    [stylePopup setTarget: self];
    
    // Set up the IODeviceMaster object to talk to the driver
    devMaster = [IODeviceMaster new];
    ret = [devMaster lookUpByDeviceName: DRIVER_NAME objectNumber:&myTag
	deviceKind:&kind];
    if (ret != IO_R_SUCCESS) { // We might not be loaded, so just log the error
	NXLogError("Couldn't find Beep device: error %d\n", ret);
    }
    
    // Be reasonable
    maxFreq = [frequencySlider maxValue];
    minFreq = [frequencySlider minValue];
    maxDuration = [durationSlider maxValue];
    minDuration = [durationSlider minValue];
    
    return self;
}

- free
{
    [devMaster free];
    return [super free];
}

// This gets called by the Configure app after we've loaded our nib
// but before it gets displayed.  We try to get values from the driver first,
// then from the table.  If those fail, we use the values in the nib.
- setTable: (NXStringTable *)instance
{
    unsigned int pitch, duration;
    const char *tempStr;
    
    table = instance;
    if([self getIntValue: &pitch forParameter: PITCH_KEY] == IO_R_SUCCESS) {
    	[frequencySlider setIntValue: pitch];
	[frequencyField setStringValue: [frequencySlider stringValue]];
    } else {
	tempStr = [table valueForStringKey: PITCH_KEY];
	strcpy(pitchString, tempStr ? tempStr : "");
	if(strlen(pitchString)) {
	    [frequencySlider setIntValue: atoi(pitchString)];
	    [frequencyField setStringValue: pitchString];
	} else {
	    [self frequencyDidChange: frequencySlider];
	}
    }
    if([self getIntValue: &duration forParameter: DURATION_KEY] == IO_R_SUCCESS) {
    	[durationSlider setIntValue: duration];
	[durationField setStringValue: [durationSlider stringValue]];
    } else {
	tempStr = [table valueForStringKey: DURATION_KEY];
	strcpy(durationString, tempStr ? tempStr : "");
	if(strlen(durationString)) {
	    [durationSlider setIntValue: atoi(durationString)];
	    [durationField setStringValue: durationString];
	} else {
	    [self durationDidChange: durationSlider];
	}
    }
    // A little redundancy for clarity
    if([self getStringValue: styleString forParameter: STYLE_KEY] == IO_R_SUCCESS) {
	[[stylePopup itemList] selectCellWithTag:
	    [stylePopup indexOfItem: styleString]];
	[styleButton setTitle: styleString];
    } else {
	tempStr = [table valueForStringKey: STYLE_KEY];
	strcpy(styleString, tempStr ? tempStr : "");
	if(strlen(styleString)) {
	    // Remember to set the tags for the MenuCells properly in IB
	    [[stylePopup itemList] selectCellWithTag:
		[stylePopup indexOfItem: styleString]];
	    [styleButton setTitle: styleString];
	} else {
	    [[stylePopup itemList] selectCellWithTag: 0];
	    [self styleDidChange: stylePopup];
	}
    }
    return self;
}

// This returns the connection to our "invisible" box in the nib
- (View *)inspectionView
{
    // Some may think this is annoying...
    NXBeep();
    return beepView;
}

// We don't use any of these...
- resourcesChanged: (IOResources *)rsrc
{
    return self;
}

// We update the table each time one of these methods gets called.
// Make sure the slider isn't continuous in IB!
- frequencyDidChange: sender
{
    int val = [sender intValue];

    if(val < minFreq) {
    	val = minFreq;
	[sender setIntValue: val];
    } else
    if(val > maxFreq) {
	val = maxFreq;
	[sender setIntValue: val];
    }
    sprintf(pitchString, "%d", val);
    [table insertKey: PITCH_KEY value: (void *)pitchString];
    if (sender == frequencySlider) {
    	[frequencyField setIntValue: val];
    } else {
	[frequencySlider setIntValue: val];
    }
    [self setIntValue: &val forParameter: PITCH_KEY];
    NXBeep();
    return self;
}

- durationDidChange: sender
{
    int val = [sender intValue];
    
    // Sanity check
    if(val < minDuration) {
    	val = minDuration;
	[sender setIntValue: val];
    }
    else
    if(val > maxDuration) {
	val = maxDuration;
	[sender setIntValue: val];
    }
    sprintf(durationString, "%d", val);
    [table insertKey: DURATION_KEY value: (void *)durationString];
    if (sender == durationSlider) {
    	[durationField setIntValue: val];
    } else {
	[durationSlider setIntValue: val];
    }
    [self setIntValue: &val forParameter: DURATION_KEY];
    NXBeep();
    return self;
}

- styleDidChange: sender
{
    strcpy(styleString, [stylePopup selectedItem]);
    [table insertKey: STYLE_KEY value: (void *)styleString];
    [self setStringValue: styleString forParameter: STYLE_KEY];
    NXBeep();
    return self;
}

- (IOReturn)getIntValue: (unsigned *)value forParameter: (IOParameterName)param
{
    IOReturn ret;
    static unsigned int one = 1;
    
    ret = [devMaster getIntValues: value forParameter: param objectNumber: myTag
	count: &one];
    if (ret != IO_R_SUCCESS) {
	NXLogError("Couldn't get value for %s", param);
	return ret;
    }
    return IO_R_SUCCESS;
}

- (IOReturn)getStringValue: (IOCharParameter)value forParameter: (IOParameterName)param
{
    IOReturn ret;
    static unsigned int len = IO_MAX_PARAMETER_ARRAY_LENGTH;
    
    ret = [devMaster getCharValues: value forParameter: param objectNumber: myTag
	count: &len];
    if (ret != IO_R_SUCCESS) {
	NXLogError("Couldn't get value for %s", param);
	return ret;
    }
    return IO_R_SUCCESS;
}

- setIntValue: (unsigned *)value forParameter: (IOParameterName)param
{
    IOReturn ret;
    
    ret = [devMaster setIntValues: value forParameter: param objectNumber: myTag
	count: 1];
    if (ret != IO_R_SUCCESS) {
	NXLogError("Couldn't set value for %s", param);
    }
    return self;
}

- setStringValue: (IOCharParameter)value forParameter: (IOParameterName)param
{
    IOReturn ret;
    
    ret = [devMaster setCharValues: value forParameter: param
    	objectNumber: myTag count: strlen(value) + 1];
    if (ret != IO_R_SUCCESS) {
	NXLogError("Couldn't set value for %s", param);
    }
    return self;
}

@end